<template>
	<view class="flex flex-col">
		<view
			v-if="model == 'line'"
			:style="[{ width: width + 'rpx', paddingTop: '16rpx', paddingBottom: '16rpx' }]"
			class="flex relative flex flex-col overflow"
		>
			<view class="relative">
				<tm-sheet
					no-level
					:round="props.round"
					:followTheme="false"
					:dark="props.dark"
					:margin="[0, 0]"
					:width="props.width"
					:height="props.height"
					:color="props.bgColor"
					:padding="[0, 0]"
				>
				</tm-sheet>
			</view>
			<view class="absolute l-0 tmRogress overflow" :style="[{ width: activeWidth + 'rpx', top: 16 + 'rpx' }]">
				<tm-sheet
					:round="props.round"
					:followTheme="props.followTheme"
					:dark="props.dark"
					:margin="[0, 0]"
					:linear="props.linear"
					:linearDeep="props.linearDeep"
					:width="activeWidth"
					:height="props.height"
					:color="props.color"
					:padding="[0, 0]"
				>
				</tm-sheet>
			</view>
			<view
				v-if="props.showBar"
				class="absolute l-0 t-0 tmRogress flex flex-col"
				:style="[
					{
						width: activeWidth + 'rpx',
						height: props.height + 32 + 'rpx',
						'align-items': 'flex-end',
						'justify-content': 'center'
					}
				]"
			>
				<slot>
					<tm-sheet
						:linear="props.linear"
						:linearDeep="props.linearDeep"
						:followTheme="props.followTheme"
						:dark="props.dark"
						:color="props.color"
						:margin="[0, 0]"
						:padding="[12, 4]"
						:round="4"
					>
						<tm-text _class="text-size-xxs" :fontSize="22" :label="props.percent + props.percentSuffix"> </tm-text>
					</tm-sheet>
				</slot>
			</view>
		</view>
		<view
			v-if="model == 'circle'"
			:style="{
				width: `${props.width}rpx`,
				height: `${props.semicircle ? props.width / 2 : props.width}rpx`
			}"
			class="flex relative flex-col"
		>
			<!-- #ifdef APP-NVUE -->
			<view v-if="showGc">
				<gcanvas
					:id="canvasId"
					:ref="canvasId"
					class="canvas"
					:style="{
						width: `${props.width}rpx`,
						height: `${props.semicircle ? props.width / 2 : props.width}rpx`
					}"
				>
				</gcanvas>
			</view>
			<!-- #endif -->
			<!-- #ifdef MP-WEIXIN  || MP-QQ -->
			<canvas
				type="2d"
				id="canvasId"
				canvas-id="canvasId"
				class="canvas"
				:style="{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : width}rpx`
				}"
			></canvas>
			<!-- #endif -->
			<!-- #ifdef MP-ALIPAY -->
			<canvas
				type="2d"
				:id="canvasId"
				:canvas-id="canvasId"
				class="canvas"
				:style="{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : width}rpx`
				}"
			></canvas>
			<!-- #endif -->
			<!-- #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ || APP-NVUE -->
			<canvas
				:id="canvasId"
				:canvas-id="canvasId"
				class="canvas"
				:style="{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : props.width}rpx`
				}"
			></canvas>
			<!-- #endif -->
			<!-- #ifndef APP-NVUE -->
			<cover-view
				:style="[
					{
						width: `${props.width}rpx`,
						height: `${props.semicircle ? props.width / 2 : props.width}rpx`
					},
					props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
					props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : ''
				]"
				class="relative absolute l-0 t-0 flex flex-col"
				:class="[!props.semicircle ? 'flex-center' : '']"
			>
				<cover-view v-if="props.showBar" :style="[{ fontSize: props.fontSize + 'rpx', color: isDark ? darkcolor : txtcolor }]">
					<slot name="title">
						{{ props.percent + props.percentSuffix }}
					</slot>
				</cover-view>
			</cover-view>
			<!-- #endif -->
			<!-- #ifdef APP-NVUE -->
			<cover-view
				:style="[
					{
						width: `${props.width}rpx`,
						height: `${props.semicircle ? props.width / 2 : props.width}rpx`
					},
					props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
					props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : ''
				]"
				class="relative absolute l-0 t-0 flex flex-col"
				:class="[!props.semicircle ? 'flex-center' : '']"
			>
				<slot name="title">
					<tm-text
						v-if="props.showBar"
						:color="props.color"
						:followTheme="props.followTheme"
						:dark="props.dark"
						:fontSize="props.fontSize"
						:label="props.percent + props.percentSuffix"
					>
					</tm-text>
				</slot>
			</cover-view>
			<!-- #endif -->
		</view>
	</view>
</template>

<script lang="ts" setup>
/**
 * 进度条
 * @description 进度条，圆形进度条，在不同的平台使用2d或者webgl方法，使得性能更加强劲。NVUE中貌似是uniapp的插件bug无法实现渐变绘制。
 * Nvue中需要在manifest.json中设置canvas模块，才能打包详见：https://github.com/dcloudio/NvueCanvasDemo
 */
import { cssstyle, tmVuetify, colorThemeType } from '../../tool/lib/interface'
import { custom_props, computedDark, computedTheme } from '../../tool/lib/minxs'
import { getCurrentInstance, computed, ref, provide, inject, onBeforeMount, onMounted, onUnmounted, nextTick, watch, PropType } from 'vue'
import tmSheet from '../tm-sheet/tm-sheet.vue'
import tmText from '../tm-text/tm-text.vue'
import tool from '../../tool/theme/theme'
import { useTmpiniaStore } from '../../tool/lib/tmpinia'
import { getCanvas } from '../../tool/function/getCanvas'
import * as TWEEN from '../../tool/lib/tween.min.js'
// #ifdef APP-NVUE
import { enable, WeexBridge } from '../../tool/gcanvas/index.js'
// #endif
const store = useTmpiniaStore()
const emits = defineEmits(['update:percent', 'change'])
const proxy = getCurrentInstance()?.proxy ?? null
const props = defineProps({
	...custom_props,
	model: {
		type: String as PropType<'line' | 'circle'>,
		default: 'line' //line,circle
	},
	//model==circle,是否是半圆。
	semicircle: {
		type: [Boolean, String],
		default: false
	},
	//model==circle有效,半圆正常是在上方。如果反转就在下方。
	semicircleFlip: {
		type: [Boolean],
		default: false
	},
	//model==circle有效
	fontSize: {
		type: [Number, String],
		default: 28
	},
	//进度 百分比数值，不带%号。也可以使用v-model:percent
	percent: {
		type: Number,
		default: 0
	},
	//数值后缀。默认为%
	percentSuffix: {
		type: String,
		default: '%'
	},
	width: {
		type: Number,
		default: 120
	},
	height: {
		type: Number,
		default: 6
	},
	bgColor: {
		type: String,
		default: 'grey-4'
	},
	color: {
		type: String,
		default: 'primary'
	},
	//是否跟随全局主题的变换而变换
	followTheme: {
		type: [Boolean, String],
		default: true
	},
	//暗黑
	dark: {
		type: [Boolean],
		default: false
	},
	linear: {
		type: [String],
		default: '' //left:右->左，right:左->右。top:下->上，bottom:上->下。
	},
	// 渐变的亮浅
	linearDeep: {
		type: [String],
		default: 'light' //light,dark,accent亮系渐变和深色渐变。
	},
	round: {
		type: [Number, String],
		default: 3
	},
	//显示数值点
	showBar: {
		type: [Boolean, String],
		default: false
	},
	disabled: {
		type: Boolean,
		default: false
	}
})
const canvasId = ref('canvasId')
// #ifndef MP-WEIXIN || MP-QQ
canvasId.value = 'tm' + String(new Date().getTime())
// #endif
let ctx: UniApp.CanvasContext
let reqId: number
let tween: any
const shadow_pr = computed(() => props.shadow * 4)
// 设置响应式全局组件库配置表。
const tmcfg = computed<tmVuetify>(() => store.tmStore)
//是否暗黑模式。
const isDark = computed(() => computedDark(props, tmcfg.value))
//计算主题
const tmcomputed = computed<cssstyle>(() => computedTheme(props, isDark.value, tmcfg.value))
const showGc = ref(false)
const sys = uni.getSystemInfoSync()
let isAndroid = false
// #ifdef APP-NVUE
if (sys.osName == 'android') {
	isAndroid = true
}
// #endif
const _bgColor = computed(
	() => computedTheme({ ...props, color: props.bgColor, followTheme: false, linear: '' }, isDark.value, tmcfg.value).backgroundColor
)
const txtcolor = tool.getColor(props.color).value
const darkcolor = tmcomputed.value.backgroundColor
const activeWidth = computed(() => {
	let pr = props.percent >= 100 ? 100 : props.percent
	let w = Math.floor((pr / 100) * props.width)
	w = w >= props.width ? props.width : w
	return w
})
const percent_rp = computed(() => {
	let pr = props.percent >= 100 ? 100 : props.percent
	return pr
})
watch(
	() => props.percent,
	(newval, oldval) => {
		if (props.disabled) return
		tween = new TWEEN.Tween({ o: oldval })
			.to({ o: newval }, 240)
			.easing(TWEEN.Easing.Linear.None)
			.onUpdate((e: any) => {
				draw(e.o)
			})
			.start()
		emits('update:percent', newval)
		emits('change', newval)
	}
)
onBeforeMount(() => {
	clearTimeout(reqId)
	reqFun()
})
onMounted(() => {
	let delay = 10
	// #ifdef APP-NVUE
	if (isAndroid) {
		delay = 250
	} else {
		delay = 100
	}
	// #endif
	// #ifdef MP
	delay = 60
	// #endif
	// #ifdef APP-VUE
	delay = 30
	// #endif
	// #ifdef APP-NVUE
	setTimeout(function () {
		showGc.value = true
		setTimeout(() => {
			getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
				.then((e) => {
					ctx = e.ctx
					tween = new TWEEN.Tween({ o: 0 })
						.to({ o: props.percent }, 240)
						.easing(TWEEN.Easing.Linear.None)
						.onUpdate((e: any) => {
							draw(e.o)
						})
						.start()
				})
				.catch((error) => {
					console.error(error)
				})
		}, delay)
	}, 200)
	// #endif

	// #ifndef APP-NVUE
	setTimeout(() => {
		getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
			.then((e) => {
				ctx = e.ctx
				tween = new TWEEN.Tween({ o: 0 })
					.to({ o: props.percent }, 240)
					.easing(TWEEN.Easing.Linear.None)
					.onUpdate((e: any) => {
						draw(e.o)
					})
					.start()
			})
			.catch((error) => {
				console.error(error)
			})
	}, delay)
	// #endif
})
onUnmounted(() => uni.$tm.u.cancelAnimationFrame(reqId))
function reqFun() {
	TWEEN.update()
	reqId = uni.$tm.u.requestAnimationFrame(reqFun)
}

function draw(bl: number = 0) {
	if (!ctx) return
	let c: any = tmcomputed.value
	const width = uni.upx2px(props.width)
	let x = width / 2
	let y = width / 2
	const borderWidth = uni.upx2px(props.height)
	const radius = x - borderWidth
	const Pi = Math.PI / 180
	let startAngle = -Pi * 90
	let endAngle = Math.PI * 2
	if (props.semicircle) {
		startAngle = -Pi * 90 * 2
		endAngle = 0
		if (props.semicircleFlip) {
			startAngle = Pi * 180
			endAngle = 0
		}
	}
	//圆环背景色
	let bgColor = _bgColor.value || '#f5f5f5'
	//圆环进度条的颜色
	let activeColor = tool.getColor(props.color).csscolor || '#ff0000'

	ctx.clearRect(0, 0, width, width)
	//先绘制背景圆;
	ctx.lineWidth = borderWidth
	ctx.strokeStyle = bgColor
	ctx.lineCap = 'round'
	ctx.beginPath()
	if (props.semicircle) {
		// 半圆
		if (props.semicircleFlip) {
			y = borderWidth / 2
			ctx.arc(x, y, radius, 0, Pi * 180, false)
		} else {
			y = y - borderWidth / 2
			ctx.arc(x, y, radius, Pi * 180, 0, false)
		}
	} else {
		ctx.arc(x, y, radius, -Math.PI / 2, 1.5 * Math.PI)
	}
	ctx.stroke()

	// 绘制圆形进度环
	const progress = Number((bl / 100).toFixed(6)) // 进度比例，0-1之间的数值
	ctx.beginPath()
	if (props.semicircle) {
		// 半圆
		if (props.semicircleFlip) {
			ctx.arc(x, y, radius, startAngle * (1 - progress), startAngle, false)
		} else {
			ctx.arc(x, y, radius, startAngle, startAngle * (1 - progress), false)
		}
	} else {
		if (props.semicircleFlip) {
			ctx.arc(x, y, radius, startAngle + endAngle * (1 - progress), startAngle + endAngle, false)
		} else {
			ctx.arc(x, y, radius, startAngle, startAngle + endAngle * progress, false)
		}
	}
	if (ctx.setLineWidth) {
		ctx.setLineWidth(borderWidth)
	} else {
		ctx.lineWidth = borderWidth
	}

	// #ifndef APP-NVUE
	//如果是渐变
	if (props.linear) {
		let gradient: any = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y * 2 - borderWidth)
		if (props.semicircle) {
			gradient = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y)
		}
		gradient.addColorStop(0, c.gradientColor[0])
		gradient.addColorStop(1, c.gradientColor[1])
		// ctx.setStrokeStyle(gradient)
		if (ctx.setStrokeStyle) {
			ctx.setStrokeStyle(gradient)
			ctx.setShadow(c.gradientColor[0])
		} else {
			ctx.strokeStyle = gradient
			ctx.shadowColor = c.gradientColor[0]
		}
	} else {
		if (ctx.setStrokeStyle) {
			ctx.setStrokeStyle(activeColor)
			ctx.setShadow(activeColor)
		} else {
			ctx.strokeStyle = activeColor
			ctx.shadowColor = activeColor
		}
	}

	// #endif
	// #ifdef APP-NVUE
	if (ctx.setStrokeStyle) {
		ctx.setStrokeStyle(activeColor)
	} else {
		ctx.strokeStyle = activeColor
	}
	// #endif

	ctx.stroke()
	if (ctx?.draw) {
		ctx.draw()
	}
}
</script>
<style>
.tmRogress {
	transition-duration: 0.34s;
	transition-timing-function: linear;
	transition-property: width;
}

/* #ifndef APP-NVUE */
cover-view {
	background: transparent;
}

/* #endif */
</style>
